import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Vector;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;


/*****************************************************************************
 * File: ServerFunc.java
 * Author:  Todd Silvia & Phil Pilosi
 * Class:   CS 441 - Spring 2011
 * Project: 2 - Distributed Booking Agent, with RMI
 * Date:    February 28th 2011
 * Description: <Insert Description Here>
 */

public class ServerImp extends UnicastRemoteObject implements ServerFunc {

		//Data members
		ArrayList<Event> listReference;
		Vector<Object> managerList;
		FileOutputStream fileOut;
		ObjectOutputStream outStream;
		String serverFile1 = "database1.txt";
		String serverFile2 = "database2.txt";
 		int eventNumber;
		int confNumber;
		boolean flip = true;
		


		public ServerImp(ArrayList<Event> myList) throws RemoteException {
			super();
			listReference = myList;
			managerList = new Vector<Object>();
			eventNumber = 1000 + listReference.size();
			confNumber = 10100;
			fileOut = null;
			outStream = null;
		}
	
		public ServerImp() throws RemoteException {
			super();
			listReference = new ArrayList<Event>();
			managerList = new Vector<Object>();
			fileOut = null;
			outStream = null;

		}
		
		/*****************************************************************************
		 * Searches for the valid event number, if found then the event is removed
		 * from the server and '1' is returned. If the event is not found then '0' is
		 * returned.
		 */
		public Object removeEvent(int theEventNumber) throws RemoteException {
			for(int i = 0; i < listReference.size(); i++) {
				if(listReference.get(i).getEventNumber() == theEventNumber) {
					listReference.remove(i);
					if(this.saveState())
					{
						System.out.println("Save successful");
					}
					else
					{
						System.out.println("Save failed");
					}
					return new PacketContainer(1);
				}
			}
			return new PacketContainer(0);
		}//END removeEvent()

		/*****************************************************************************
		 * The corresponding event number is provided along with the name for the reservation
		 * and the space that is requested for the Reservation. If the space is available
		 * then the reservation is made and the space remaining in the event is changed.
		 * If the event cannot be found or the reservation is asking for too much space then
		 * '0' is returned.
		 */
		public PacketContainer makeReservation(int theEventNumber, int spaceRequested, String theName) throws RemoteException {
			for(int i = 0; i < this.listReference.size(); i++) {
				if(theEventNumber == listReference.get(i).getEventNumber()) {
					if(listReference.get(i).getSpaceRemaining() - spaceRequested >= 0) {
						confNumber++;
						listReference.get(i).addReservation(new Reservation(theName, spaceRequested, confNumber));
						listReference.get(i).setSpaceRemaining(spaceRequested);
						this.startBroadcast("***Reservation Added - Event: " + theEventNumber + " Space Taken: " + 
											spaceRequested + " Space Remaining: " + listReference.get(i).getSpaceRemaining() +
											"***");
						if(this.saveState())
						{
							System.out.println("Save successful");
						}
						else
						{
							System.out.println("Save failed");
						}
						return new PacketContainer(confNumber);
					}
				}
			}
			return new PacketContainer(0);
		}//END makeReservation()

		/*****************************************************************************
		 * Searches through all of the events and then all of the reservations of that
		 * given event and finds the reservation if the reservation is found then it is
		 * removed from the event, the event add the space to spaceRemaining based on 
		 * how much space was taken by the event, and then '1' is returned if it is
		 * successful and '0' is returned if something didn't work.
		 */
		public Object deleteReservation(int theEventNumber, int theConfirmationNumber) throws RemoteException {
			for(int i = 0; i < listReference.size(); i++) {
				//LOOP to check against all events
				if(theEventNumber == listReference.get(i).getEventNumber()) {
					for(int j = 0; j < listReference.get(i).getReservationList().size(); j++) {
						//LOOP to check against all reservations with in the Event
						if(listReference.get(i).getReservationList().get(j).getConfirmationNumber() == theConfirmationNumber) {
							listReference.get(i).setSpaceRemaining((-1) * listReference.get(i).getReservationList().get(j).getSpaceReserved());
							//Send out broadcast to all managers that a reservation has been deleted
							this.startBroadcast("***Reservation Deleted - Event: " + theEventNumber + " Space Freed: " + 
									listReference.get(i).getReservationList().get(j).getSpaceReserved() + " Space Remaining: " + listReference.get(i).getSpaceRemaining() +
									"***");
							listReference.get(i).getReservationList().remove(j);
							if(this.saveState()) {
								System.out.println("Save successful");
							}
							else {
								System.out.println("Save failed");
							}
							return new PacketContainer(1);
						}
					}
				}
			}
			return new PacketContainer(0);
		}//END deleteReservation()

		/*****************************************************************************
		 * Returns the list of all events currently in the server to the client
		 */
		public Object viewEvents() throws RemoteException {
			return new PacketContainer(this.listReference);
		}//END viewEvents()
		
		/*****************************************************************************
		 * 
		 */
		public synchronized void connect(ClientCallbackInterface clientInfo) throws RemoteException {

			//Checks to see if the manager is already in the list.
			if(!(managerList.contains(clientInfo))) {
				managerList.addElement(clientInfo);
				System.out.println("Added new manager to broadcast list");
			}
			else {
				System.out.println("This manager is already in the list");
			}
		}//END connect()

		/*****************************************************************************
		 * 
		 */
		public synchronized void disconnect(ClientCallbackInterface clientInfo) throws RemoteException {
			if(managerList.removeElement(clientInfo)){
				System.out.println("Manager has disconnected");
			}
			else{
				System.out.println("Manager attempted to unregister, but was not found.");
			}
					
		}//END disconnect()

		/*****************************************************************************
		 * 
		 */
		private synchronized void startBroadcast(String message) throws RemoteException
		{
			System.out.println("Broadcasting to managers");
			for(int index=0; index < managerList.size(); index++)
			{
				ClientCallbackInterface currentManager;
				currentManager = (ClientCallbackInterface)managerList.elementAt(index);
				currentManager.broadcastMessage(message);

			}

			System.out.println("Done broadcasting!");
		}//END startBroadcast()
		
		/*****************************************************************************
		 * 
		 */
		public synchronized boolean saveState()
		{
			String serverFile;
			System.out.println("Save state called");
			try {
				if(flip) {
					serverFile = serverFile1;
					flip = !flip;
				}
				else {
					serverFile = serverFile2;
					flip = !flip;
				}
				fileOut = new FileOutputStream(serverFile);
				outStream = new ObjectOutputStream(fileOut);
				outStream.writeObject(listReference);
				outStream.close();
				return true;
			}
			catch(Exception e)
			{
				System.out.println("An error happened while creating save state");
				return false;
			}
			
		}//END saveState()

		/*****************************************************************************
		 * Takes the Event object passed in and adds it to the database, after the event
		 * is added then a unique event number is returned to the manager.
		 */
		public PacketContainer addEvent(Event eventToAdd) throws RemoteException {
			eventNumber++;
			eventToAdd.setEventNumber(eventNumber);
			listReference.add(eventToAdd);
			System.out.println("Event Added D: " + eventToAdd.getEventDescription() + " S: " + eventToAdd.getEventSpace());//<dflag>


			if(this.saveState()) {
				System.out.println("Save successful");
			}
			else{
				System.out.println("Save failed");
			}

			return new PacketContainer(eventNumber);
		}//END addEvent()

		/*****************************************************************************
		 * The client asks for an event by event number, if the event is found then
		 * it is returned to the client. If the event is not found then null is returned.
		 */
		public PacketContainer askEvent(int theEventNumber) throws RemoteException {
			for(int i = 0; i < this.listReference.size(); i++) {
				if(theEventNumber == listReference.get(i).getEventNumber()) {
					return new PacketContainer(listReference.get(i));
				}
			}
			return null;
		}//END askEvent()
		
		/*****************************************************************************
		 * The client is asking to retrieve a reservation of a given event. An event 
		 * number as well as a confirmation number is provided and if the reservation
		 * exists then it is returned, if it doesn't exist then null is returned.
		 */
		public PacketContainer askReservation(int theEventNumber, int theConfNumber) {
			for(int i = 0; i < listReference.size(); i++) {
				//LOOP through events searching for input theEventNumber
				if(listReference.get(i).getEventNumber() == theEventNumber) {
					for(int j = 0; j < listReference.get(i).getReservationList().size(); j++) {
						//LOOP through Reservation list for the event to search for the theConfNumber
						if(listReference.get(i).getReservationList().get(j).getConfirmationNumber() == theConfNumber) {
							//Reservation was found and it is returned
							return new PacketContainer(listReference.get(i).getReservationList().get(j));
						}
					}
				}
			}
			return null;	//Reservation was not found so null is returned
		}//END askReservation()
		
}//END ServerImp{}
